package com.example.activitispringbootstest.service.impl;

import com.example.activitispringbootstest.common.Processable;
import com.example.activitispringbootstest.service.ProcessTaskService;
import com.example.activitispringbootstest.util.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author swxu_2005@163.com
 * @date 2019/2/4
 */
@Slf4j
@Service
public class ProcessTaskServiceImpl implements ProcessTaskService {

  @Autowired
  private TaskService taskService;

  @Autowired
  private RepositoryService repositoryService;

  @Autowired
  private RuntimeService runtimeService;

  @Autowired
  private IdentityService identityService;

  /**
   * 系统中的主键是Long，但Activiti里用的是String，故需要转换
   * @return
   */
  private static String getCurrentUserId() {
    return SecurityUser.getUserId().toString();
  }

  /**
   * 启动流程
   * @param bussinessObj 业务实例
   * @param key          流程key
   * @param variables    流程参数
   */
  @Override
  public void startProcess(Processable bussinessObj, String key, Map<String, Object> variables) {
    String bussinessKey = bussinessObj.getId().toString();
    identityService.setAuthenticatedUserId(getCurrentUserId());
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, bussinessKey, variables);
    // 将流程实例ID填到业务实例的processInstanceId字段，形成互相关联
    bussinessObj.setProcessInstanceId(processInstance.getId());
    log.info("processInstance = {}", processInstance);
  }

  /**
   * 查询当前用户的待办任务列表
   * 若task的assignee字段有值，则当前用户是待办人，可直接完成任务
   * 若task的assignee字段为空，则当前用户是候选人，需要先签收，再完成任务
   * @param key    流程key
   */
  @Override
  public List<Task> queryTodoTasks(String key) {
    return taskService.createTaskQuery()
        .processDefinitionKey(key)
        .taskCandidateOrAssigned(getCurrentUserId())
        .orderByTaskCreateTime()
        .desc()
        .listPage(0, 10);
  }

  /**
   * 查询当前用户的待办任务列表
   */
  @Override
  public List<Task> queryTodoTasks() {
    return taskService.createTaskQuery()
        .taskCandidateOrAssigned(getCurrentUserId())
        .orderByTaskCreateTime()
        .desc()
        .listPage(0, 10);
  }

  /**
   * 查询指定流程实例的所有审批记录
   */
  @Override
  public void queryHisTasks() {
    // TODO
  }

  /**
   * 签收指定任务
   * @param taskId             任务ID
   */
  @Override
  public void claimTask(String taskId) {
    // 校验当前用户是否为指定任务的候选人
    Task targetTask = taskService.createTaskQuery().taskId(taskId).singleResult();
    List<String> candidates = new ArrayList<>();
    List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
    for (IdentityLink identityLink : identityLinks) {
      candidates.add(identityLink.getUserId());
    }
    if (!candidates.contains(getCurrentUserId())) {
      throw new RuntimeException("当前用户不是候选人");
    }

    // 签收指定任务
    taskService.claim(taskId, getCurrentUserId());
  }

  /**
   * 完成指定任务
   * @param taskId             任务ID
   * @param processInstanceId  流程实例ID
   * @param comment            评论
   * @param variables          流程参数
   */
  @Override
  public void completeTask(String taskId, String processInstanceId, String comment, Map<String, Object> variables) {
    // 校验指定任务的assignee是否为当前用户
    Task targetTask = taskService.createTaskQuery().taskId(taskId).singleResult();
    String assignee = targetTask.getAssignee();
    if (StringUtils.isEmpty(assignee) ||
        !StringUtils.isEmpty(assignee) && !assignee.equals(getCurrentUserId())) {
      throw new RuntimeException("当前用户不是指定执行人");
    }

    // 完成指定任务
    if (!StringUtils.isEmpty(processInstanceId) && !StringUtils.isEmpty(comment)) {
      taskService.addComment(taskId, processInstanceId, comment);
    }
    taskService.complete(taskId, variables);
  }

  /**
   * 完成指定任务
   * @param taskId             任务ID
   * @param variables          流程参数
   */
  @Override
  public void completeTask(String taskId, Map<String, Object> variables) {
    this.completeTask(taskId, null, null, variables);
  }

  /**
   * 完成指定任务
   * @param taskId             任务ID
   */
  @Override
  public void completeTask(String taskId) {
    this.completeTask(taskId, null, null, null);
  }
}
